package com.gopay.cashier.web.controller.cloud;

import java.math.BigDecimal;
import java.util.Date;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.gopay.cashier.common.exception.WebException;
import com.gopay.cashier.domain.bean.LoginUser;
import com.gopay.cashier.web.command.BankPayWay;
import com.gopay.cashier.web.command.ExpressPayChannelCommand;
import com.gopay.cashier.web.command.ExpressPoundageVo;
import com.gopay.cashier.web.utils.CashierConfig;
import com.gopay.common.cipher.utils.LoggerMaskUtils;
import com.gopay.common.constants.bank.BisBankBusinessProd;
import com.gopay.common.constants.bank.ExPayBankConstants;
import com.gopay.common.constants.cust.CustPersonInfoConstants;
import com.gopay.common.constants.cust.UserInfoConstants;
import com.gopay.common.constants.ep.EpBindState;
import com.gopay.common.constants.ep.EpCardType;
import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.constants.trans.PayChannel;
import com.gopay.common.constants.trans.TxnStaCode;
import com.gopay.common.constants.trans.UserType;
import com.gopay.common.domain.UserInfo;
import com.gopay.common.domain.bank.PayRequest;
import com.gopay.common.domain.bis.BisBankInfoMain;
import com.gopay.common.domain.cps.CpsGenMainOrder;
import com.gopay.common.domain.cust.CustPersonInfo;
import com.gopay.common.domain.ep.EpCardBindInfo;
import com.gopay.common.domain.poundage.PoundageRes;
import com.gopay.common.exception.CPSException;
import com.gopay.common.exception.GopayException;
import com.gopay.common.util.JsonUtils;
import com.gopay.common.util.RequestUtils;
import com.gopay.remote.bank.BisBaseExPayInterfaceRemoteService;
import com.gopay.remote.bank.BisCertificationRemoteService;
import com.gopay.remote.bank.ExPayInteractRemoteService;
import com.gopay.remote.cps.expresspay.EpBankCardRemoteService;
import com.gopay.remote.order.ReceiptOrderUpdateRemoteService;

/**
 * Created by HJY on 2015/5/12.
 */
@Controller
@RequestMapping("/cloud")
public class CloudExpressBaseController extends CloudBaseController {

    @Resource(name = "epBankCardService")
    protected EpBankCardRemoteService epBankCardService;

    @Resource(name = "exAuthRemoteService")
    protected ExPayInteractRemoteService exAuthRemoteService;

    @Resource(name = "exPayRemoteService")
    protected BisBaseExPayInterfaceRemoteService exPayRemoteService;

    @Resource(name = "receiptOrderUpdateService")
    protected ReceiptOrderUpdateRemoteService receiptOrderUpdateService;

    @Resource(name = "bisCertificationRemoteService")
    protected BisCertificationRemoteService bisCertificationRemoteService;
    
    protected static final String PORTAL_URL = CashierConfig.get(CashierConfig.PORTAL_URL);
    protected static final String CLOUD_URL = CashierConfig.get(CashierConfig.CLOUD_URL);

    protected PayRequest getPayRequest(String orderId,ExpressPayChannelCommand command) throws GopayException {
        CpsGenMainOrder order = getMainOrder(orderId, true);
        PayRequest base = new PayRequest();
        base.setBank(order.getOutStlmId());
        base.setOrderId(orderId);
        base.setBankBusProd(BisBankBusinessProd._15.value);
        base.setPhoneNo(command.getExMobileNo());
        base.setCardNo(command.getBankCardNo());
        base.setCardHolder(command.getExHolder());
        base.setCidNo(command.getExIdCardNo());
        base.setCidType(CustPersonInfoConstants.CERT_TYPE_IDCARD);
        base.setValidateCode(command.getExSmsCode());
        if(StringUtils.equals(order.getPayChannel(), PayChannel._06.toString())){
            base.setCvv2(command.getExCvn2());
            base.setAvailableDate(command.getExValidDate());
            base.setBankBusProd(BisBankBusinessProd._05.value);
        }
        try{
            PayRequest log = new PayRequest();
            BeanUtils.copyProperties(log,base);
            if(StringUtils.equals(order.getPayChannel(), PayChannel._06.toString()))
                log.setCvv2("****");
            logger.info("exPayRequest orderId :"+orderId+" Info:"+ JsonUtils.toJson(log));
        }catch (Exception e){ }

        base.setBrowser(ExPayBankConstants.VAL_BROWSER_VERSION_NUMBER);
        base.setIp(RequestUtils.getRemoteRealIpAddr(getRequest()));
        base.setTermination(ExPayBankConstants.TERMINAL_TYPE_PC);
        return  base;
    }

    protected ExpressPayChannelCommand getExCommand(ExpressPayChannelCommand command,EpCardBindInfo info){
        if(info == null)
            return  command;

        PayChannel cardPaych = info.getBankAcctType() == EpCardType.CREDIT ? PayChannel._06 :PayChannel._03;
        if(cardPaych == PayChannel._06){
            /*String date = DateUtils.format(info.getCreditCardExpired(), "MMyy");
            command.setExValidDate(date);*/
        	 command.setExValidDate(info.getCreditCardExpired());
        }

        command.setExMobileNo(info.getBindMobile());
        command.setBankCardNo(info.getBankAcctNum());
        command.setExHolder(info.getBankAcctName());
        command.setExIdCardNo(info.getCertNo());
        command.setExPayChannel(cardPaych.value);
        return command;
    }


    protected   String getHiddenCardNo(String cardNo){
        if(cardNo == null || cardNo.length()<9)
            return "";
        int certNoLen = cardNo.length();
        String pass = "";
        for(int i=0;i<certNoLen-8;i++){
            pass = pass.concat("*");
        }
        return cardNo.substring(0,4).concat(pass).concat(cardNo.substring(certNoLen-4,certNoLen));
    }

    protected  String getHiddenMobileNo(String mobileNo){
        if(mobileNo == null || mobileNo.length()<11)
            return "";
        int certNoLen = mobileNo.length();
        String pass = "";
        for(int i=0;i<4;i++){
            pass = pass.concat("*");
        }
        return mobileNo.substring(0,3).concat(pass).concat(mobileNo.substring(certNoLen-4,certNoLen));
    }

    protected EpCardBindInfo createEpCardBindInfo(ExpressPayChannelCommand exCommand,String custId){
        EpCardBindInfo bindInfo = new EpCardBindInfo();
        EpCardType epCardType = StringUtils.equals(exCommand.getExPayChannel(), PayChannel._06.toString()) ? EpCardType.CREDIT : EpCardType.DEBIT;
        bindInfo.setCustId(custId);
        bindInfo.setBindMobile(exCommand.getExMobileNo());
        bindInfo.setCertType(CustPersonInfoConstants.CERT_TYPE_IDCARD);
        bindInfo.setCertNo(exCommand.getExIdCardNo());
        bindInfo.setBankAcctName(exCommand.getExHolder());
        bindInfo.setBankAcctNum(exCommand.getBankCardNo());
        bindInfo.setBankAcctType(epCardType);
        bindInfo.setBankCode(exCommand.getExPayBankCode());
        bindInfo.setBindState(EpBindState.TEMP);
        if(StringUtils.equals(exCommand.getExPayChannel(), PayChannel._06.toString())){
            String vd = exCommand.getExValidDate();
//            String month = vd.substring(0,2);
//            String year = vd.substring(2,4);
           // bindInfo.setCreditCardExpired(DateUtils.strToDate("20".concat(year + "").concat(month), "yyyyMM"));
            bindInfo.setCreditCardExpired(vd);
        }
        bindInfo.setBindTime(new Date());
        bindInfo = epBankCardService.bindEpCardInfo(bindInfo);
        return bindInfo;
    }


    @RequestMapping(value = "/00100/express/progress.shtml")
    public ModelAndView expreeProgress(String orderId,String inOrderId,String t) throws WebException {
        ModelAndView mv = new ModelAndView("/cloud/00100/common/progress");
        CpsGenMainOrder order = getMainOrder(orderId, true);
        if (null == order) {
            logger.error("找不到订单！order not exits!" + orderId);
            throw new WebException(ProcCodeConstants.PROC_CODE_100E5041);
        }
        if (TxnStaCode.TXN_STA_CD_30000.value == (order.getTxnStaCd())) {
            logger.error("订单状态不对！txn_sta_cd wrong!" + orderId);
            throw new WebException(ProcCodeConstants.PROC_CODE_100E5042);
        }
        String result = (TxnStaCode.TXN_STA_CD_20000.value == order.getTxnStaCd())?"1":"0";
        mv.addObject("orderId",orderId);
        StringBuffer sb = new StringBuffer();
        sb.append("/cloud/00100/express/result.shtml?orderKey="+getRequest().getParameter("orderKey")+"&t="+t+"&orderId="+orderId);
        if(StringUtils.isNotBlank(inOrderId))
            sb.append("&inOrderId="+inOrderId);
        mv.addObject("redirectUrl",sb.toString());
        mv.addObject("result",result);
        return  mv;
    }

    @RequestMapping(value = "/00100/express/result.shtml")
    public ModelAndView expressResult(String orderId,String inOrderId,String t) throws WebException {
        CpsGenMainOrder order = cpsGeneralMainOrderQueryDAO.find(CpsGenMainOrder.class,orderId);
        if(order == null)
            throw new WebException(ProcCodeConstants.PROC_CODE_100E5041);

        if (TxnStaCode.TXN_STA_CD_30000.value == (order.getTxnStaCd())) {
            logger.error("订单状态不对！txn_sta_cd wrong!" + orderId);
            throw new WebException(ProcCodeConstants.PROC_CODE_100E5042);
        }
        ModelAndView mv = new ModelAndView();
        mv.addObject("portalUrl",PORTAL_URL);
        if(TxnStaCode.TXN_STA_CD_20000.value == order.getTxnStaCd()){
            boolean isFirst = false;
            if(StringUtils.isNotBlank(t) && !StringUtils.equals(t,"0")){
                EpCardBindInfo bindInfo = epBankCardService.getCardInfoById(t);
                if(bindInfo != null){
                    CustPersonInfo custInfo =  custPersonInfoQueryManager.get(bindInfo.getCustId());
                    if(StringUtils.equals(custInfo.getCustStat(), CustPersonInfoConstants.CUST_STAT_NORMAL) && StringUtils.isBlank(custInfo.getPayPwd())){
                        isFirst = true;
                        mv.setViewName("/00100/content/express/firstPayRes");
                        BisBankInfoMain bankInfo = bisBankInfoService.getByCode(bindInfo.getBankCode());
                        String bankName = bankInfo.getBankName();
                        String type = "储蓄卡";
                        String exPayChannel = PayChannel._03.value;
                        if(bindInfo.getBankAcctType() == EpCardType.CREDIT){
                            type = "信用卡";
                            exPayChannel = PayChannel._06.value;
                        }
                        String exPayBankName = bankName.concat(type);
                        mv.addObject("hideExMobileNo",getHiddenMobileNo(bindInfo.getBindMobile()));
                        mv.addObject("exPayBankCode",bindInfo.getBankCode());
                        mv.addObject("exPayChannel",exPayChannel);
                        mv.addObject("exPayBankName",exPayBankName);
                        mv.addObject("hideBankCardNo",getHiddenCardNo(bindInfo.getBankAcctNum()));

                        mv.addObject("custId",bindInfo.getCustId());
                        mv.addObject("orderId",orderId);
                    }
                }
            }
            if(!isFirst)
                mv.setViewName("/cloud/00100/common/success");
        }else{
            mv.setViewName("/cloud/00100/common/fail");
        }
        mv.addObject("order", getOrderInfo(orderId));
        orderId = StringUtils.isNotBlank(inOrderId) ? inOrderId :orderId;
        mv.addObject("notifyMerUrl","/redirect/BankPayCallbackSelf.shtml?orderKey="+getRequest().getParameter("orderKey")+"&orderId="+orderId);
        logOut();
        return mv;
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = { Exception.class })
    protected EpCardBindInfo bindCardInfo(ExpressPayChannelCommand exCommand) throws GopayException {
        boolean bind = false;
        EpCardBindInfo bindInfo = null;
        if(StringUtils.isNotBlank(exCommand.getExCardId())){
            //已绑定卡
            return epBankCardService.getCardInfoById(exCommand.getExCardId());
        }

        CustPersonInfo personInfo = getExpressUser(exCommand);
        if (personInfo != null ) {
            if (
               (( CustPersonInfoConstants.REAL_NAME_CERTIFY_HALF.equals(personInfo.getIsRealNameCertify())
                    ||CustPersonInfoConstants.REAL_NAME_CERTIFY_YES.equals(personInfo.getIsRealNameCertify())
                ) && StringUtils.equals(personInfo.getCertNo(),exCommand.getExIdCardNo()))
               || (CustPersonInfoConstants.REAL_NAME_CERTIFY_NO.equals(personInfo.getIsRealNameCertify()))){
                bind = true;
            }
        }
        if(bind){
            bindInfo = epBankCardService.getBindCardInfoByCustIdBankNo(personInfo.getCustId(),exCommand.getBankCardNo());
            if(bindInfo == null){
                bindInfo = createEpCardBindInfo(exCommand,personInfo.getCustId());
            }else{
                boolean freeze = epBankCardService.isFreezeCardNo(bindInfo.getBankAcctNum());
                if(freeze){
                    logger.error(this.getClass() + bindInfo.getBankAcctNum()+" was freezed " + exCommand);
                    throw new WebException(ProcCodeConstants.PROC_CODE_100E5118);
                }

                if(!StringUtils.equals(bindInfo.getBindMobile(),exCommand.getExMobileNo())){
                    bindInfo.setBindMobile(exCommand.getExMobileNo());
                    epBankCardService.updateEpCardBindInfo(bindInfo);
                }
            }
        }
        return bindInfo;
    }


    private CustPersonInfo getExpressUser(ExpressPayChannelCommand command) throws GopayException {
        CustPersonInfo pinfo = null;
        try {
            LoginUser loginUser = getLoginUser();
            if(loginUser != null){
                if (UserType.Person.equals(UserType.get(loginUser.getUser().getUserType()))) {
                    return  custPersonInfoQueryManager.get(loginUser.getUser().getCustId());
                }
                return null;//企业用户
            }

            if (allCustInfoQueryDAO.isCustCorpMobile(command.getExMobileNo())) {
                return null;//企业用户
            }

            pinfo = custPersonInfoQueryManager.getByCustMob(command.getExMobileNo());
            if(pinfo != null){
                logger.error("createFastUser-->has the same ccf custId :"+pinfo.getCustId()+";custMob:"+pinfo.getCustMob());
                return pinfo;
            }
            CustPersonInfo personInfo = new CustPersonInfo();
            personInfo.setRegType(CustPersonInfoConstants.REG_TYPE_MOBILE);
            personInfo.setCertNo(command.getExIdCardNo());
            personInfo.setCertType(CustPersonInfoConstants.CERT_TYPE_IDCARD);
            personInfo.setCustMob(command.getExMobileNo());
            personInfo.setCustName(command.getExHolder());
            personInfo.setMobileCertifyTime(new Date());
            personInfo.setLastUpdTs(new Date());
            personInfo.setCreatedTs(new Date());
            personInfo.setCustStat(CustPersonInfoConstants.CUST_STAT_WAIT_EMAIL_CERTIFY);
            personInfo.setIsRealNameCertify(CustPersonInfoConstants.REAL_NAME_CERTIFY_NO);
            personInfo.setIsMobileCertify("1");

            UserInfo userInfo = new UserInfo();
            userInfo.setCreatedTs(new Date());
            userInfo.setOperMobile(command.getExMobileNo());
            userInfo.setOperName(command.getExHolder());
            userInfo.setUserType(UserType.Person.value);
            userInfo.setUserStat(UserInfoConstants.USER_STATE_AWAIT_EMAIL_VERIFY);
            userInfo.setOperId("999");
            userInfo.setLastUpdTs(new Date());
            userInfo.setCreatedTs(new Date());
            userInfo.setRegTime(new Date());
            userInfo.setIspwdQuesLock(UserInfoConstants.PWD_STAT_NORMAL);
            userInfo.setPwdErrNum(0);
            userInfo.setPwdQuesErrNum(0);
            userInfo.setSecuretyLevel(new BigDecimal(UserInfoConstants.SECURETY_LEVEL_PASSWORD));
            UserInfo newUserInfo = userRemoteService.saveCustPersonUser(personInfo,userInfo);
            pinfo = custPersonInfoQueryManager.get(newUserInfo.getCustId());
            logger.info("create new Express User custId:"+pinfo.getCustId() +" for orderId:"+getRequest().getParameter(GOPAY_ORDER_ID));
        } catch (CPSException e) {
            e.printStackTrace();
            throw new GopayException(ProcCodeConstants.PROC_CODE_200E9002);
        }
        return pinfo;
    }

    protected ExpressPoundageVo getPoundageRes(CpsGenMainOrder order,ExpressPayChannelCommand command) throws WebException {
        ExpressPoundageVo vo  = new ExpressPoundageVo();
        LoginUser user = getLoginUser();

        if(user == null){
            //未登录，银行卡支付
            if(command.getExPayWay() != BankPayWay.A && command.getExPayChannel() != null){
                PayChannel payChannel = PayChannel.get(command.getExPayChannel());
                vo.setPayChannel(payChannel);
                vo.setPoundageRes(getPoundageRes(order, payChannel));
                return vo;
            }
        }else{
            //已登录，先充值后支付，比较手续费
            PayChannel payChannel1 = getPayChannel();
            PoundageRes res1 = getPoundageRes(order, payChannel1);
            vo.setPayChannel(payChannel1);
            vo.setPoundageRes(res1);
            if(command.getExPayWay() != BankPayWay.A && command.getExPayChannel() != null){
                PayChannel payChannel2 =  PayChannel.get(command.getExPayChannel());
                PoundageRes res2 = getPoundageRes(order, payChannel2);
                int comp = res1.getTotalFeeAmt().compareTo(res2.getTotalFeeAmt());
                if(comp ==-1 || comp ==0){
                    vo.setPayChannel(payChannel2);
                    vo.setPoundageRes(res2);
                }
            }
            return  vo;
        }
        throw new WebException(ProcCodeConstants.PROC_CODE_100E5051);
    }

    protected PayChannel getPayChannel(){
        PayChannel payChannel = null;
        LoginUser loginUser = getLoginUser();
        if(loginUser!=null){
            UserType userType = UserType.get(loginUser.getUser().getUserType());
            if (UserType.Person.equals(userType)) {
                payChannel = PayChannel._02;
            } else {
                payChannel = PayChannel._52;
            }
        }
        return payChannel;
    }

    @Async
    protected void updatePayOrder(String orderId,String cardNo) throws GopayException{

        if(StringUtils.isNotBlank(orderId) && StringUtils.isNotBlank(cardNo)){
            receiptOrderUpdateService.updateEpBankCardNo(orderId, cardNo);
        }else{
            //modified by zyt,2017-2-28 日志中卡号做掩码
            logger.error("updatePayOrder is error orderId:"+orderId +";cardNo:"+LoggerMaskUtils.maskString(cardNo));
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E9204);
        }
    }

    @RequestMapping(value = "/00100/express/timeout.shtml")
    public ModelAndView timeout(String orderId){
        logger.error("orderId:"+orderId+" is timeout!");
        return  new ModelAndView("/00100/common/timeout");
    }
}
